﻿#define _CRT_SRCURE_NO_WARNINGS 1
#pragma  warning (disable:4996)
#pragma once
#include <iostream>
using namespace std;


//class Date
//{
//public:
//	Date(const Date& d)// 用引用会很舒服，不像指针还得解引用才能拿到对象本身，我们直接用引用就可以拿到对象本身了，要指针干嘛。
//	{
//		cout << "调用拷贝构造函数" << endl;
//		_year = d._year;
//		_month = d._month;
//		_day = d._day;
//
//		//d._day = _day;//不加const，有的人可能会把拷贝这个地方给写反了，会导致对象d中的成员变量被搞成随机值。
//		//不加const可以，加上const更好，防止低级问题的发生。
//	}
////bool operator==(Date* this, const Date& d2)
//	bool operator==(const Date& d)//这里用传值传参不好，因为值拷贝要调用拷贝构造，而且比较里面不会改变对象，所以要用const修饰
//	{
//		//成员变量是私有的，我们怎么解决呢？
//		//放到类里面由于this指针的存在，导致参数过多，因为这个函数只允许两个参数存在
//		return d._year == _year &&
//			d._month == _month &&
//			d._day == _day;
//		//传过来之后，直接用对象访问就可以了，因为传过来，这段函数是知道左参数是对象的，直接用对象访问。
//		//所以这里是非常灵活的，如果当作内联处理没有this指针，我可以通过对象来访问，不当作内联处理，有this指针，我可以通过this指针访问。
//		//本人觉得，编译器对这个地方肯定有优化，应该不会同意我们的内联请求
//	}
//	bool operator>(const Date& d)
//	{
//		if (_year > d._year)
//		{
//			return true;
//		}
//		else if (_year == d._year && _month > d._month)
//		{
//			return true;
//		}
//		else if (_year == d._year && _month == d._month && _day > d._day)
//		{
//			return true;
//		}
//		return false;
//	}
//	bool operator>=(const Date& d)
//	{
//		return *this > d || *this == d;
//	}
	//Date& operator+=(int day)//没有引用返回，这里就是值返回，又需要调用拷贝构造函数
	//{
	//	_day += day;
	//	while (_day > GetMonthDay(_year, _month))
	//	{
	//		_day -= GetMonthDay(_year, _month);
	//		_month++;
	//		if (_month == 13)
	//		{
	//			++_year;
	//			_month = 1;
	//		}
	//	}
	//	return *this;
	//}

	//Date operator+(int day)//没有引用返回，这里就是值返回，需要
	//{
	//	Date ret(*this);
	//	ret._day += day;
	//	while (ret._day > GetMonthDay(ret._year, ret._month))
	//	{
	//		ret._day -= GetMonthDay(ret._year, ret._month);
	//		ret._month++;
	//		if (ret._month == 13)
	//		{
	//			++ret._year;
	//			ret._month = 1;
	//		}
	//	}
	//	return ret;
	//}
//	int GetMonthDay(int year, int month)
//	{
//		static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
//		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
//		{
//			return 29;
//		}
//		else
//		{
//			return monthDayArray[month];
//		}
//	}
//	Date(int year = 1, int month = 1, int day = 1)
//	{
//		_year = year;
//		_month = month;
//		_day = day;
//
//		//检查日期是否合法
//		if (!(year >= 1
//			&& (month >= 1 && month <= 12)
//			&& (day >= 1 && day <= GetMonthDay(year, month))))
//		{
//			cout << "非法日期" << endl;
//		}
//		
//	}
//	void Print()
//	{
//		cout << _year << "/" << _month << "/" << _day << endl;
//	}
//	// 运算符的操作数有几个，运算符重载函数的参数就有几个，+有两个操作数，两个参数，this指针，类类型参数，++一个操作上，一个参数this指针
//
//	//等于重载和赋值重载不是一个东西
//	Date& operator=(const Date& d)// 不加引用会调用一次拷贝构造，这是无用的消耗
//	{
//		if (this != &d)//防止自己给自己赋值，随机值将原有数据覆盖掉
//		{
//			_year = d._year;
//			_month = d._month;
//			_day = d._day;
//		}
//		
//		return *this;
//	}
//private:
//	int _year;
//	int _month;
//	int _day;
//};
//class Stack
//{
//public:
//	Stack(int capacity = 4)
//	{
//		cout << "Stack(int capacity = 4)" << endl;
//
//		_a = (int*)malloc(sizeof(int) * capacity);
//		if (_a == nullptr)
//		{
//			perror("malloc fail");
//			exit(-1);
//		}
//
//		_top = 0;
//		_capacity = capacity;
//	}
//	Stack(const Stack& st)
//	{
//		_a = (int*)malloc(sizeof(int) * st._capacity);
//		if (_a == nullptr)
//		{
//			perror("malloc fail");
//			exit(-1);
//		}
//		memcpy(_a, st._a, sizeof(int) * st._top);
//		_top = st._top;
//		_capacity = st._capacity;
//
//	}
//	//  st1 = st2;
//	//  st1 = st1;
//	Stack& operator=(const Stack& st)
//	{
//		//这里存在三种情况，st1的空间大小和st2的空间大小，><=三种情况
//		//所以这里有一个解决的办法，直接把st1free掉，重新开一块儿空间。所以这个地方和拷贝构造还是很相似的。
//		if (this != &st)// 防止自己给自己赋值
//		{
//			free(_a);
//			_a = (int*)malloc(sizeof(int) * st._capacity);//开一个和st2一样大的空间，然后把数据拷过去。
//			if (_a == nullptr)
//			{
//				perror("malloc fail");
//				exit(-1);
//			}
//			memcpy(_a, st._a, sizeof(int) * st._top);
//			_top = st._top;
//			_capacity = st._capacity;
//		}
//			
//		return *this;
//	}
//	~Stack()
//	{
//		cout << "~Stack()" << endl;
//
//		free(_a);
//		_a = nullptr;
//		_top = _capacity = 0;
//	}
//
//	void Push(int x)
//	{
//		// ....
//		// 扩容
//		_a[_top++] = x;
//	}
//
//private:
//	int* _a;//缺省值也可以用malloc开辟一个空间，注意：他就是一个缺省值，这个缺省值就放在那里，并不会调用malloc，就写了个样子在那里。
//	//int* _a = (int*)malloc(sizeof(int) * 4);
//	int _top;
//	int _capacity;//声明是不会占空间的，只有实例化对象时才会开辟空间。
//
//};
//
//class MyQueue {
//public:
//	void push(int x)
//	{
//		_pushST.Push(x);
//	}
//private:
//	Stack _pushST;
//	Stack _popST;//对于自定义类型，调用该类的赋值重载
//
//	size_t _size;//对于内置类型完成值拷贝
//};



class Date
{
	//友元声明（类的任意位置，不用加inline），我是你的朋友，我来偷你家🍑桃子了，你别放狗咬我。
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
public:
	
	int GetMonthDay(int year, int month)//频繁调用，我们把它搞成内联
	{
		static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			return 29;
		}
		else
		{
			return monthDayArray[month];
		}
	}
	Date(int year = 1, int month = 1, int day = 1)// 频繁调用，搞成内联
	{
		_year = year;
		_month = month;
		_day = day;

		//检查日期是否合法
		if (!(year >= 1
			&& (month >= 1 && month <= 12)
			&& (day >= 1 && day <= GetMonthDay(year, month))))
		{
			cout << "非法日期" << endl;
		}
		
	}
	void Print()const// 相当于const Date* const this，这样this指向的内容就是可读了，这里只修饰this指向的内容，其他参数不受影响
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	bool operator==(const Date& d)const;//内联是不能声明和定义分离的，因为可能产生链接性错误，所以这里不是内联，就是普通的函数调用
	bool operator>(const Date& d)const;
	bool operator>=(const Date& d)const;
	bool operator<=(const Date& d)const;
	bool operator<(const Date& d)const;
	bool operator!=(const Date& d)const;

	// d1+=100
	Date& operator+=(int day);

	// d1+100
	Date operator+(int day)const;

	// d1-=100
	Date& operator-=(int day);

	// d1-100
	Date operator-(int day)const;

	// C++规定，前置的运算符重载
	Date& operator++();

	// C++规定，后置的运算符重载，加一个参数int，这就是后置++，但实际上这个参数没什么用，因为只需要this这一个隐形参数就够了。
	Date operator++(int);

// 他们构成函数重载，同时也是运算符重载，
// 运算符重载就是为了让自定义类型能够用运算符，增强代码的可读性，因为它可以隐讳的去调用函数来处理自定义类型之间的运算。
// 函数重载是为了同名函数同时存在
	Date& operator--();

	Date operator--(int);

	//d1-d2两个日期中间差了多少天
	int operator-(const Date& d)const;
	
	// cout << d1
	//void operator<<(ostream& out)//两个对象，cout和d1
	//{
	//	out << _year << "年" << _month << "月" << _day << "日" << endl;
	//}
	
	// 要求这个类的对象不让取地址
	Date* operator&()
	{
	  return nullptr;
		//return this;
	}
	const Date* operator&()const//const Date* const this
	{
	  return nullptr;
		//return this;
	}
private:
	int _year;
	int _month;
	int _day;

};


// this指针是的的确确存在的，只不过当作内联的时候，this指针就只是作为一个cpu指令放到代码段，而不当作内联的时候，他所在的成员函数
//会建立函数栈帧，这时候this就会存在于栈区上。


//**************<<和>>运算符重载一般不写成成员函数，因为this默认抢了第一个参数位置，那左操作数就得是日期类的对象，但这不符合使用习惯和可读性。
//d1<<cout



// operator<<(cout,d1)  cout<<d1
inline ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;//成员是公有的才可以这么用
	return out;
}
//总结起来就一句话，无论inline修饰的函数是否被当作内联处理，他的函数名和有效地址都是不进符号表的，链接期间不会产生冲突。
inline istream& operator>>(istream& in, Date& d)
{	
	in >> d._year >> d._month >> d._day;
	return in;
}
//注意这里和std中的运算符重载不会发生命名冲突的，因为库中的参数类型肯定是没有指针的，具体原因是.h文件要在Test.cpp和Date.cpp展开，
//所以就会发生重定义的问题，它是自己和自己冲突了，不是和库里面冲突了。


//*******所以这是一个典型的问题，只要你在.h文件里面定义全局函数或全局变量，都会发生这样重定义的问题，因为这个文件要展开两次。
//*******两个源文件的符号表中就都有全局函数或变量的地址，然后我们一链接这是就会有两个相同的函数名和地址，编译器就会报错：一个或多个重定义符号

//我们能用上面这种方式来解决自定义类型的输出，就是因为cout只支持内置类型的输出，所以我们需要将自定义类型中的内置类型一个一个输出，
//也就是将自定义类型拆分成内置类型，然后利用C++给我们提供的库里面的实例化对象来一个一个完成拆分的内置类型的输出，这就增强了代码的可读性
//因为我们看到的就是cout这个对象将我们的自定义类型一下子就输出了，但究其原因其实是因为我们调用了流插入运算符的重载。

//*******所以自定义类型没什么厉害的，他们都是基于内置类型封装起来的，我们只不过利用了运算符重载将自定义类型的操作看起来像内置类型的操作。


//上面问题的三个解决方式：
//1.声明和定义分离，这样符号表就只有一个有有效地址的函数名，test中的函数名地址是无效的。
//2.将全局函数搞成静态的
//全局函数本身具有外部链接属性，加个static后就变为内部链接属性，其他源文件无法使用，也就是这个全局函数的函数名是不进符号表的，不参与链接过程。
//只要源文件包含.h，那这个文件里面就有这个全局函数，并且这个全局函数不允许其他源文件使用，这也就失去了链接的必要，因为链接就是为了源文件之间
//的函数能够互通互相使用，自然他的函数名就不进符号表。
//3.将声明和定义都放到.h文件，给他搞成内联函数，因为它代码短，调用还频繁，如果不加inline就会发生命名冲突。

//这里推荐声明和定义分离来解决这样的问题。



//**************结论：.h文件尽量不要定义全局的变量和函数，否则会由于符号表中函数的重命名，产生链接问题



//内联和static的原理是一样的，他们定义的函数都不进符号表。一个是没办法进，一个是不能进。


//对于私有成员变量无法访问的解决办法：
//1.写一个公有的成员函数，利用公有的成员函数拿到私有的成员变量，Java喜欢这么写，C++也可以这么写，但C++喜欢用友元函数。









































































































